NexusPi Git Node
native/reboot.cpp HEAD (592c826d) Text, 11.29 KB
T8b949e// Copyright (C) 2026, Chad Attermann
T8b949e//
T8b949e// See reboot.h for the deferred-reboot rationale. Cleanup sequence mirrors
T8b949e// meshtasticd's Power::reboot() (src/Power.cpp:711-737 in their tree):
T8b949e// notify, close API server, release peripheral handles, SPI.end(), reboot().
T8b949e// Our daemon doesn't have a notification observer pattern yet, so we skip
T8b949e// that step.
T8b949e//
T8b949e// Re-exec note: we deliberately bypass Portduino's `::reboot()`. That
T8b949e// function does `execv(progArgv[0], progArgv)` with the raw argv[0] the
T8b949e// kernel handed main() — when the daemon is launched via PATH (e.g.
T8b949e// systemd's `rnoded` or a bare command-name invocation) argv[0] is the
T8b949e// bare name, and execv() does NOT consult PATH, so it fails with ENOENT
T8b949e// and Portduino then exit()s. We resolve our own absolute path from the
T8b949e// kernel (/proc/self/exe on Linux, _NSGetExecutablePath on macOS) and
T8b949e// reconstruct argv from the kernel too (/proc/self/cmdline on Linux,
T8b949e// KERN_PROCARGS2 on macOS) so flags like --fsroot survive the re-exec.
Tff7b72#Tff7b72include T8b949e"reboot.h"
Tff7b72#Tff7b72include T8b949e"EEPROMShim.h"
Tff7b72#Tff7b72include T8b949e"TCPHostInterface.h"
Tff7b72#Tff7b72if defined(ENABLE_WEBSOCKETS) && __has_include(<WiFi.h>)
Tff7b72#Tff7b72include T8b949e"../WebSocketConsole.h"
Tff7b72#Tff7b72endif
Tff7b72#Tff7b72include T8b949e<Arduino.h>T8b949e // extern HardwareSPI SPI;
Tff7b72#Tff7b72include T8b949e<cerrno>
Tff7b72#Tff7b72include T8b949e<cstdio>
Tff7b72#Tff7b72include T8b949e<cstdlib>
Tff7b72#Tff7b72include T8b949e<cstring>
Tff7b72#Tff7b72include T8b949e<dirent.h>
Tff7b72#Tff7b72include T8b949e<limits.h>T8b949e // PATH_MAX
Tff7b72#Tff7b72include T8b949e<string>
Tff7b72#Tff7b72include T8b949e<unistd.h>
Tff7b72#Tff7b72include T8b949e<vector>
Tff7b72#Tff7b72if defined(__APPLE__)
Tff7b72#Tff7b72include T8b949e<mach-o/dyld.h>
Tff7b72#Tff7b72include T8b949e<sys/sysctl.h>
Tff7b72#Tff7b72endif
Tff7b72namespace T7ee787native_pinmap Tb4b4b4{
T8b949e// Defined in PinMap.cpp. No-op on macOS, releases libgpiod handles
T8b949e// on Linux by rebinding each previously-bound pin to a SimGPIOPin
T8b949e// (which triggers ~LinuxGPIOPin → gpiod_line_release).
Tffa657void Td2a8ffrelease_linux_gpiosTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
T8b949e// Captured in native/main.cpp::portduinoSetup() before the chdir(data_dir).
T8b949e// We need to restore this before execv() so the child's load() can resolve
T8b949e// `rnoded.conf` against the same cwd as the original invocation.
Tff7b72extern Te6edf3stdTff7b72:Tff7b72:Te6edf3string Te6edf3g_launch_cwdTb4b4b4;
Tff7b72namespace T7ee787native_reboot Tb4b4b4{
Tff7b72namespace Tb4b4b4{
Tffa657bool Te6edf3reboot_flag Tff7b72= Tffa657falseTb4b4b4;
Tff7b72#Tff7b72if defined(__linux__)
Te6edf3stdTff7b72:Tff7b72:Te6edf3string Td2a8ffresolve_exe_pathTb4b4b4(Tb4b4b4) Tb4b4b4{
Tffa657char Te6edf3bufTb4b4b4[Te6edf3PATH_MAXTb4b4b4]Tb4b4b4;
Tffa657ssize_t Te6edf3n Tff7b72= Tff7b72:Tff7b72:Te6edf3readlinkTb4b4b4(Ta5d6ff"Ta5d6ff/proc/self/exeTa5d6ff"Tb4b4b4, Te6edf3bufTb4b4b4, Tff7b72sizeofTb4b4b4(Te6edf3bufTb4b4b4) Tff7b72- T79c0ff1Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3n Tff7b72<Tff7b72= T79c0ff0Tb4b4b4) Tff7b72return Tb4b4b4{Tb4b4b4}Tb4b4b4;
Te6edf3bufTb4b4b4[Te6edf3nTb4b4b4] Tff7b72= Ta5d6ff'Ta5d6ff\0Ta5d6ff'Tb4b4b4;
Tff7b72return Te6edf3stdTff7b72:Tff7b72:Te6edf3stringTb4b4b4(Te6edf3bufTb4b4b4)Tb4b4b4;
Tb4b4b4}
Te6edf3stdTff7b72:Tff7b72:Te6edf3vectorTff7b72<Te6edf3stdTff7b72:Tff7b72:Te6edf3stringTff7b72> Te6edf3resolve_argvTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3stdTff7b72:Tff7b72:Te6edf3vectorTff7b72<Te6edf3stdTff7b72:Tff7b72:Te6edf3stringTff7b72> Te6edf3outTb4b4b4;
Te6edf3stdTff7b72:Tff7b72:Tffa657FILETff7b72* Te6edf3f Tff7b72= Te6edf3stdTff7b72:Tff7b72:Te6edf3fopenTb4b4b4(Ta5d6ff"Ta5d6ff/proc/self/cmdlineTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffrbTa5d6ff"Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3fTb4b4b4) Tff7b72return Te6edf3outTb4b4b4;
Te6edf3stdTff7b72:Tff7b72:Te6edf3string Te6edf3curTb4b4b4;
Tffa657int Te6edf3cTb4b4b4;
Tff7b72while Tb4b4b4(Tb4b4b4(Te6edf3c Tff7b72= Te6edf3stdTff7b72:Tff7b72:Te6edf3fgetcTb4b4b4(Te6edf3fTb4b4b4)Tb4b4b4) Tff7b72!Tff7b72= Te6edf3EOFTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3c Tff7b72=Tff7b72= T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3outTb4b4b4.Te6edf3push_backTb4b4b4(Te6edf3stdTff7b72:Tff7b72:Te6edf3moveTb4b4b4(Te6edf3curTb4b4b4)Tb4b4b4)Tb4b4b4;
Te6edf3curTb4b4b4.Te6edf3clearTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3curTb4b4b4.Te6edf3push_backTb4b4b4(Tff7b72static_castTff7b72<Tffa657charTff7b72>Tb4b4b4(Te6edf3cTb4b4b4)Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}
Te6edf3stdTff7b72:Tff7b72:Te6edf3fcloseTb4b4b4(Te6edf3fTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3curTb4b4b4.Te6edf3emptyTb4b4b4(Tb4b4b4)Tb4b4b4) Te6edf3outTb4b4b4.Te6edf3push_backTb4b4b4(Te6edf3stdTff7b72:Tff7b72:Te6edf3moveTb4b4b4(Te6edf3curTb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72return Te6edf3outTb4b4b4;
Tb4b4b4}
Tff7b72#Tff7b72elif defined(__APPLE__)
Te6edf3stdTff7b72:Tff7b72:Te6edf3string Te6edf3resolve_exe_pathTb4b4b4(Tb4b4b4) Tb4b4b4{
Tffa657char Te6edf3bufTb4b4b4[Te6edf3PATH_MAXTb4b4b4]Tb4b4b4;
Tffa657uint32_t Te6edf3size Tff7b72= Tff7b72sizeofTb4b4b4(Te6edf3bufTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3_NSGetExecutablePathTb4b4b4(Te6edf3bufTb4b4b4, Tff7b72&Te6edf3sizeTb4b4b4) Tff7b72!Tff7b72= T79c0ff0Tb4b4b4) Tff7b72return Tb4b4b4{Tb4b4b4}Tb4b4b4;
T8b949e// _NSGetExecutablePath returns a path that may include `..` or symlinks;
T8b949e// canonicalize so the child sees the same exe regardless of how the
T8b949e// parent was launched.
Tffa657char Te6edf3realTb4b4b4[Te6edf3PATH_MAXTb4b4b4]Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72:Tff7b72:Te6edf3realpathTb4b4b4(Te6edf3bufTb4b4b4, Te6edf3realTb4b4b4)Tb4b4b4) Tff7b72return Te6edf3stdTff7b72:Tff7b72:Te6edf3stringTb4b4b4(Te6edf3realTb4b4b4)Tb4b4b4;
Tff7b72return Te6edf3stdTff7b72:Tff7b72:Te6edf3stringTb4b4b4(Te6edf3bufTb4b4b4)Tb4b4b4;
Tb4b4b4}
Te6edf3stdTff7b72:Tff7b72:Te6edf3vectorTff7b72<Te6edf3stdTff7b72:Tff7b72:Te6edf3stringTff7b72> Te6edf3resolve_argvTb4b4b4(Tb4b4b4) Tb4b4b4{
T8b949e// KERN_PROCARGS2 layout (Darwin):
T8b949e// int argc
T8b949e// char exe_path[] (NUL-terminated, may be NUL-padded for alignment)
T8b949e// char argv[0][] (NUL-terminated)
T8b949e// ...
T8b949e// char argv[argc-1][]
T8b949e// char envp[0][] ... (we stop after argc strings)
Te6edf3stdTff7b72:Tff7b72:Te6edf3vectorTff7b72<Te6edf3stdTff7b72:Tff7b72:Te6edf3stringTff7b72> Te6edf3outTb4b4b4;
Tffa657int Te6edf3mibTb4b4b4[T79c0ff3Tb4b4b4] Tff7b72= Tb4b4b4{ Te6edf3CTL_KERNTb4b4b4, Te6edf3KERN_PROCARGS2Tb4b4b4, Tff7b72:Tff7b72:Te6edf3getpidTb4b4b4(Tb4b4b4) Tb4b4b4}Tb4b4b4;
Tffa657size_t Te6edf3size Tff7b72= T79c0ff0Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72:Tff7b72:Te6edf3sysctlTb4b4b4(Te6edf3mibTb4b4b4, T79c0ff3Tb4b4b4, Tffa657nullptrTb4b4b4, Tff7b72&Te6edf3sizeTb4b4b4, Tffa657nullptrTb4b4b4, T79c0ff0Tb4b4b4) Tff7b72!Tff7b72= T79c0ff0 Tff7b72|Tff7b72| Te6edf3size Tff7b72=Tff7b72= T79c0ff0Tb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3outTb4b4b4;
Tb4b4b4}
Te6edf3stdTff7b72:Tff7b72:Te6edf3vectorTff7b72<Tffa657charTff7b72> Te6edf3bufTb4b4b4(Te6edf3sizeTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72:Tff7b72:Te6edf3sysctlTb4b4b4(Te6edf3mibTb4b4b4, T79c0ff3Tb4b4b4, Te6edf3bufTb4b4b4.Te6edf3dataTb4b4b4(Tb4b4b4)Tb4b4b4, Tff7b72&Te6edf3sizeTb4b4b4, Tffa657nullptrTb4b4b4, T79c0ff0Tb4b4b4) Tff7b72!Tff7b72= T79c0ff0Tb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3outTb4b4b4;
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3size Tff7b72< Tff7b72sizeofTb4b4b4(Tffa657intTb4b4b4)Tb4b4b4) Tff7b72return Te6edf3outTb4b4b4;
Tffa657int Te6edf3argc Tff7b72= T79c0ff0Tb4b4b4;
Te6edf3stdTff7b72:Tff7b72:Te6edf3memcpyTb4b4b4(Tff7b72&Te6edf3argcTb4b4b4, Te6edf3bufTb4b4b4.Te6edf3dataTb4b4b4(Tb4b4b4)Tb4b4b4, Tff7b72sizeofTb4b4b4(Te6edf3argcTb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3argc Tff7b72<Tff7b72= T79c0ff0Tb4b4b4) Tff7b72return Te6edf3outTb4b4b4;
Tff7b72const Tffa657charTff7b72* Te6edf3p Tff7b72= Te6edf3bufTb4b4b4.Te6edf3dataTb4b4b4(Tb4b4b4) Tff7b72+ Tff7b72sizeofTb4b4b4(Tffa657intTb4b4b4)Tb4b4b4;
Tff7b72const Tffa657charTff7b72* Te6edf3end Tff7b72= Te6edf3bufTb4b4b4.Te6edf3dataTb4b4b4(Tb4b4b4) Tff7b72+ Te6edf3sizeTb4b4b4;
Tff7b72while Tb4b4b4(Te6edf3p Tff7b72< Te6edf3end Tff7b72&Tff7b72& Tff7b72*Te6edf3p Tff7b72!Tff7b72= Ta5d6ff'Ta5d6ff\0Ta5d6ff'Tb4b4b4) Tff7b72+Tff7b72+Te6edf3pTb4b4b4; T8b949e// skip exe string
Tff7b72while Tb4b4b4(Te6edf3p Tff7b72< Te6edf3end Tff7b72&Tff7b72& Tff7b72*Te6edf3p Tff7b72=Tff7b72= Ta5d6ff'Ta5d6ff\0Ta5d6ff'Tb4b4b4) Tff7b72+Tff7b72+Te6edf3pTb4b4b4; T8b949e// skip alignment NULs
Tff7b72for Tb4b4b4(Tffa657int Te6edf3i Tff7b72= T79c0ff0Tb4b4b4; Te6edf3i Tff7b72< Te6edf3argc Tff7b72&Tff7b72& Te6edf3p Tff7b72< Te6edf3endTb4b4b4; Tff7b72+Tff7b72+Te6edf3iTb4b4b4) Tb4b4b4{
Tff7b72const Tffa657charTff7b72* Te6edf3s Tff7b72= Te6edf3pTb4b4b4;
Tff7b72while Tb4b4b4(Te6edf3p Tff7b72< Te6edf3end Tff7b72&Tff7b72& Tff7b72*Te6edf3p Tff7b72!Tff7b72= Ta5d6ff'Ta5d6ff\0Ta5d6ff'Tb4b4b4) Tff7b72+Tff7b72+Te6edf3pTb4b4b4;
Te6edf3outTb4b4b4.Te6edf3emplace_backTb4b4b4(Te6edf3sTb4b4b4, Tff7b72static_castTff7b72<Tffa657size_tTff7b72>Tb4b4b4(Te6edf3p Tff7b72- Te6edf3sTb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3p Tff7b72< Te6edf3endTb4b4b4) Tff7b72+Tff7b72+Te6edf3pTb4b4b4; T8b949e// step over NUL
Tb4b4b4}
Tff7b72return Te6edf3outTb4b4b4;
Tb4b4b4}
Tff7b72#Tff7b72else
T8b949e// Unknown POSIX host — try /proc as a last resort, fall back to empty.
Te6edf3stdTff7b72:Tff7b72:Te6edf3string Te6edf3resolve_exe_pathTb4b4b4(Tb4b4b4) Tb4b4b4{
Tffa657char Te6edf3bufTb4b4b4[Te6edf3PATH_MAXTb4b4b4]Tb4b4b4;
Tffa657ssize_t Te6edf3n Tff7b72= Tff7b72:Tff7b72:Te6edf3readlinkTb4b4b4(Ta5d6ff"Ta5d6ff/proc/self/exeTa5d6ff"Tb4b4b4, Te6edf3bufTb4b4b4, Tff7b72sizeofTb4b4b4(Te6edf3bufTb4b4b4) Tff7b72- T79c0ff1Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3n Tff7b72<Tff7b72= T79c0ff0Tb4b4b4) Tff7b72return Tb4b4b4{Tb4b4b4}Tb4b4b4;
Te6edf3bufTb4b4b4[Te6edf3nTb4b4b4] Tff7b72= Ta5d6ff'Ta5d6ff\0Ta5d6ff'Tb4b4b4;
Tff7b72return Te6edf3stdTff7b72:Tff7b72:Te6edf3stringTb4b4b4(Te6edf3bufTb4b4b4)Tb4b4b4;
Tb4b4b4}
Te6edf3stdTff7b72:Tff7b72:Te6edf3vectorTff7b72<Te6edf3stdTff7b72:Tff7b72:Te6edf3stringTff7b72> Te6edf3resolve_argvTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72return Tb4b4b4{Tb4b4b4}Tb4b4b4;
Tb4b4b4}
Tff7b72#Tff7b72endif
T8b949e// Close inherited spidev fds before execv. Surgical replacement for a
T8b949e// blanket "close every fd above stderr" sweep: blanket closing turned out
T8b949e// to break libgpiod re-binding in the child (likely because we closed
T8b949e// something Portduino still needed during shutdown / inside its own
T8b949e// destructors). Here we walk /proc/self/fd (Linux) / /dev/fd (macOS) and
T8b949e// only close fds whose target path is /dev/spidev*.
T8b949e//
T8b949e// Why this matters: Portduino's LinuxHardwareSPI::end() calls spiChip.reset()
T8b949e// but a static SPI_map<string, shared_ptr> still holds the LinuxSPIChip ref,
T8b949e// so its PosixFile base never destructs. PosixFile opens without O_CLOEXEC,
T8b949e// so the spidev fd is inherited by the re-exec'd child, which then races
T8b949e// the kernel spidev driver with its own fresh open — the SX126x reads back
T8b949e// 0x00 across all SPI ioctls. Explicitly closing the spidev fd before
T8b949e// execv breaks the race.
T8b949e//
T8b949e// Other potentially-leaked fds (libgpiod chips not closed by ~LinuxGPIOPin,
T8b949e// AsyncUDP sockets, etc.) we leave alone here. libgpiod chip fds in
T8b949e// particular are closed by release_linux_gpios() destroying every bound
T8b949e// LinuxGPIOPin, which is called right before this. If a different fd
T8b949e// turns out to also need cleanup later, add it to the prefix match.
Tffa657void Te6edf3close_inherited_spidev_fdsTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72#Tff7b72if defined(__linux__)
Tff7b72const Tffa657charTff7b72* Te6edf3fd_dir Tff7b72= Ta5d6ff"Ta5d6ff/proc/self/fdTa5d6ff"Tb4b4b4;
Tff7b72#Tff7b72elif defined(__APPLE__)
Tff7b72const Tffa657charTff7b72* Te6edf3fd_dir Tff7b72= Ta5d6ff"Ta5d6ff/dev/fdTa5d6ff"Tb4b4b4;
Tff7b72#Tff7b72else
Tff7b72returnTb4b4b4;
Tff7b72#Tff7b72endif
Tff7b72#Tff7b72if defined(__linux__) || defined(__APPLE__)
Tffa657DIRTff7b72* Te6edf3d Tff7b72= Tff7b72:Tff7b72:Te6edf3opendirTb4b4b4(Te6edf3fd_dirTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3dTb4b4b4) Tff7b72returnTb4b4b4;
Tff7b72const Tffa657int Te6edf3dfd Tff7b72= Tff7b72:Tff7b72:Te6edf3dirfdTb4b4b4(Te6edf3dTb4b4b4)Tb4b4b4;
Tff7b72struct T56d364direntTff7b72* Te6edf3eTb4b4b4;
Tff7b72while Tb4b4b4(Tb4b4b4(Te6edf3e Tff7b72= Tff7b72:Tff7b72:Te6edf3readdirTb4b4b4(Te6edf3dTb4b4b4)Tb4b4b4) Tff7b72!Tff7b72= Tffa657nullptrTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3eTff7b72-Tff7b72>Te6edf3d_nameTb4b4b4[T79c0ff0Tb4b4b4] Tff7b72< Ta5d6ff'Ta5d6ff0Ta5d6ff' Tff7b72|Tff7b72| Te6edf3eTff7b72-Tff7b72>Te6edf3d_nameTb4b4b4[T79c0ff0Tb4b4b4] Tff7b72> Ta5d6ff'Ta5d6ff9Ta5d6ff'Tb4b4b4) Tff7b72continueTb4b4b4;
Tff7b72const Tffa657int Te6edf3fd Tff7b72= Te6edf3stdTff7b72:Tff7b72:Te6edf3atoiTb4b4b4(Te6edf3eTff7b72-Tff7b72>Te6edf3d_nameTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3fd Tff7b72<Tff7b72= T79c0ff2 Tff7b72|Tff7b72| Te6edf3fd Tff7b72=Tff7b72= Te6edf3dfdTb4b4b4) Tff7b72continueTb4b4b4;
Tffa657char Te6edf3fdpathTb4b4b4[T79c0ff64Tb4b4b4]Tb4b4b4;
Te6edf3stdTff7b72:Tff7b72:Te6edf3snprintfTb4b4b4(Te6edf3fdpathTb4b4b4, Tff7b72sizeofTb4b4b4(Te6edf3fdpathTb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ff%s/%sTa5d6ff"Tb4b4b4, Te6edf3fd_dirTb4b4b4, Te6edf3eTff7b72-Tff7b72>Te6edf3d_nameTb4b4b4)Tb4b4b4;
Tffa657char Te6edf3targetTb4b4b4[Te6edf3PATH_MAXTb4b4b4]Tb4b4b4;
Tff7b72const Tffa657ssize_t Te6edf3n Tff7b72= Tff7b72:Tff7b72:Te6edf3readlinkTb4b4b4(Te6edf3fdpathTb4b4b4, Te6edf3targetTb4b4b4, Tff7b72sizeofTb4b4b4(Te6edf3targetTb4b4b4) Tff7b72- T79c0ff1Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3n Tff7b72<Tff7b72= T79c0ff0Tb4b4b4) Tff7b72continueTb4b4b4;
Te6edf3targetTb4b4b4[Te6edf3nTb4b4b4] Tff7b72= Ta5d6ff'Ta5d6ff\0Ta5d6ff'Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3stdTff7b72:Tff7b72:Te6edf3strncmpTb4b4b4(Te6edf3targetTb4b4b4, Ta5d6ff"Ta5d6ff/dev/spidevTa5d6ff"Tb4b4b4, T79c0ff11Tb4b4b4) Tff7b72=Tff7b72= T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[reboot] closing inherited %s (fd=%d)Tffea00\nTa5d6ff"Tb4b4b4,
Te6edf3targetTb4b4b4, Te6edf3fdTb4b4b4)Tb4b4b4;
Tff7b72:Tff7b72:Te6edf3closeTb4b4b4(Te6edf3fdTb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}
Tff7b72:Tff7b72:Te6edf3closedirTb4b4b4(Te6edf3dTb4b4b4)Tb4b4b4;
Tff7b72#Tff7b72endif
Tb4b4b4}
Tb4b4b4} T8b949e// namespace
Tffa657void Te6edf3requestTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3reboot_flagTb4b4b4) Tb4b4b4{
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4,
Ta5d6ff"Ta5d6ff[reboot] requested — will re-exec on next loop tickTffea00\nTa5d6ff"Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Te6edf3reboot_flag Tff7b72= Tffa657trueTb4b4b4;
Tb4b4b4}
Tffa657bool Te6edf3pendingTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3reboot_flagTb4b4b4;
Tb4b4b4}
Tb4b4b4[Tb4b4b4[Te6edf3noreturnTb4b4b4]Tb4b4b4] Tffa657void Te6edf3performTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[reboot] tearing down and re-execing daemonTffea00\nTa5d6ff"Tb4b4b4)Tb4b4b4;
T8b949e// Defensive flush. Most config-save paths in Utilities.h call
T8b949e// EEPROM.commit() before hard_reset(), but a missed call site would
T8b949e// otherwise lose the property write on re-exec.
Te6edf3EEPROMTb4b4b4.Te6edf3commitTb4b4b4(Tb4b4b4)Tb4b4b4;
T8b949e// Close the WebSocket listener so the re-exec'd process can re-bind
T8b949e// port 8080. FD_CLOEXEC on the listen fd would handle it too — this
T8b949e// is the explicit-teardown half of belt and suspenders.
Tff7b72#Tff7b72if defined(ENABLE_WEBSOCKETS) && __has_include(<WiFi.h>)
Te6edf3ws_consoleTff7b72:Tff7b72:Te6edf3shutdownTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72#Tff7b72endif
T8b949e// Close the KISS-over-TCP listener + active client so the re-exec'd
T8b949e// process can re-bind the same port without waiting for TIME_WAIT.
Te6edf3native_kiss_tcpTff7b72:Tff7b72:Te6edf3shutdownTb4b4b4(Tb4b4b4)Tb4b4b4;
T8b949e// Release libgpiod chip/line handles. Without this, the re-exec'd
T8b949e// process fails to re-acquire the same lines (EBUSY).
Te6edf3native_pinmapTff7b72:Tff7b72:Te6edf3release_linux_gpiosTb4b4b4(Tb4b4b4)Tb4b4b4;
T8b949e// Let Portduino tear down the SimSPIChip (cross_platform) or the
T8b949e// /dev/spidev binding (Linux). On Linux this is not actually enough —
T8b949e// LinuxHardwareSPI holds the spidev fd in a static SPI_map shared_ptr
T8b949e// that end() doesn't clear, and PosixFile opens without O_CLOEXEC — the
T8b949e// close_inherited_spidev_fds() sweep below is what actually closes the fd.
Te6edf3SPITb4b4b4.Te6edf3endTb4b4b4(Tb4b4b4)Tb4b4b4;
T8b949e// Close any inherited /dev/spidev* fd so the child doesn't race the
T8b949e// kernel spidev driver on re-open. See close_inherited_spidev_fds()
T8b949e// for the full rationale.
Te6edf3close_inherited_spidev_fdsTb4b4b4(Tb4b4b4)Tb4b4b4;
T8b949e// Restore the original launch cwd. portduinoSetup() chdir'd to data_dir
T8b949e// after loading config; without this restore the child's load() looks
T8b949e// for rnoded.conf in data_dir, doesn't find it, and silently falls back
T8b949e// to defaults — wrong pin map, radio doesn't respond.
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3g_launch_cwdTb4b4b4.Te6edf3emptyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72:Tff7b72:Te6edf3chdirTb4b4b4(Te6edf3g_launch_cwdTb4b4b4.Te6edf3c_strTb4b4b4(Tb4b4b4)Tb4b4b4) Tff7b72!Tff7b72= T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[reboot] chdir(%s) failed: %s — child may Ta5d6ff"
Ta5d6ff"Ta5d6ffnot find rnoded.confTffea00\nTa5d6ff"Tb4b4b4,
Te6edf3g_launch_cwdTb4b4b4.Te6edf3c_strTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3stdTff7b72:Tff7b72:Te6edf3strerrorTb4b4b4(Te6edf3errnoTb4b4b4)Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}
T8b949e// Resolve our own absolute path from the kernel — see file-top comment.
Te6edf3stdTff7b72:Tff7b72:Te6edf3string Te6edf3exe Tff7b72= Te6edf3resolve_exe_pathTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3exeTb4b4b4.Te6edf3emptyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4,
Ta5d6ff"Ta5d6ff[reboot] could not resolve own exe path — abortTffea00\nTa5d6ff"Tb4b4b4)Tb4b4b4;
Te6edf3stdTff7b72:Tff7b72:Te6edf3abortTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
T8b949e// Reconstruct argv so --fsroot etc. survive the re-exec. If the kernel
T8b949e// gives us nothing (rare), fall back to a single-arg exec — lossy but
T8b949e// better than dying.
Te6edf3stdTff7b72:Tff7b72:Te6edf3vectorTff7b72<Te6edf3stdTff7b72:Tff7b72:Te6edf3stringTff7b72> Te6edf3argv_strs Tff7b72= Te6edf3resolve_argvTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3argv_strsTb4b4b4.Te6edf3emptyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3argv_strsTb4b4b4.Te6edf3emplace_backTb4b4b4(Te6edf3exeTb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72else Tb4b4b4{
T8b949e// Ensure argv[0] agrees with the resolved path. Conventional and
T8b949e// makes ps output less confusing across the re-exec.
Te6edf3argv_strsTb4b4b4[T79c0ff0Tb4b4b4] Tff7b72= Te6edf3exeTb4b4b4;
Tb4b4b4}
T8b949e// std::string::data() doesn't return non-const char* until C++17; use
T8b949e// &s[0] for C++14 compatibility (non-const since C++11, including for
T8b949e// empty strings where it points at the NUL terminator).
Te6edf3stdTff7b72:Tff7b72:Te6edf3vectorTff7b72<Tffa657charTff7b72*Tff7b72> Te6edf3argvTb4b4b4;
Te6edf3argvTb4b4b4.Te6edf3reserveTb4b4b4(Te6edf3argv_strsTb4b4b4.Te6edf3sizeTb4b4b4(Tb4b4b4) Tff7b72+ T79c0ff1Tb4b4b4)Tb4b4b4;
Tff7b72for Tb4b4b4(Tff7b72autoTff7b72& Te6edf3s Tff7b72: Te6edf3argv_strsTb4b4b4) Te6edf3argvTb4b4b4.Te6edf3push_backTb4b4b4(Tff7b72&Te6edf3sTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4)Tb4b4b4;
Te6edf3argvTb4b4b4.Te6edf3push_backTb4b4b4(Tffa657nullptrTb4b4b4)Tb4b4b4;
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[reboot] re-exec %s (argc=%zu)Tffea00\nTa5d6ff"Tb4b4b4,
Te6edf3exeTb4b4b4.Te6edf3c_strTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3argv_strsTb4b4b4.Te6edf3sizeTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72:Tff7b72:Te6edf3execvTb4b4b4(Te6edf3exeTb4b4b4.Te6edf3c_strTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3argvTb4b4b4.Te6edf3dataTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4;
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[reboot] execv(%s) failed: %s — abortTffea00\nTa5d6ff"Tb4b4b4,
Te6edf3exeTb4b4b4.Te6edf3c_strTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3stdTff7b72:Tff7b72:Te6edf3strerrorTb4b4b4(Te6edf3errnoTb4b4b4)Tb4b4b4)Tb4b4b4;
Te6edf3stdTff7b72:Tff7b72:Te6edf3abortTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4} T8b949e// namespace native_reboot
Tffa657void Te6edf3native_request_rebootTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3native_rebootTff7b72:Tff7b72:Te6edf3requestTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tffa657bool Te6edf3native_reboot_pendingTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3native_rebootTff7b72:Tff7b72:Te6edf3pendingTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4[Tb4b4b4[Te6edf3noreturnTb4b4b4]Tb4b4b4] Tffa657void Te6edf3native_reboot_performTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3native_rebootTff7b72:Tff7b72:Te6edf3performTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Served by rngit 1.5.2 - Generated in 0.03s